﻿using JiveMessenger.Models;
using JiveMessenger.ViewModels;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Documents;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Windows.ApplicationModel;
using System.Diagnostics;
using System.Threading.Tasks;
using Windows.UI;
using System.Net.Http;
using GalaSoft.MvvmLight.Messaging;
using Windows.Foundation;
using GalaSoft.MvvmLight.Threading;
using System.IO;
using Windows.Storage;
using System.Collections.Concurrent;

namespace JiveMessenger.Utilities
{
    /// <summary>
    /// Adds auto-hyperlinking, images and emoticons to a RichTextBlock control.
    /// </summary>
    public static class RichTextBlockExtension
    {

        //private static ConcurrentDictionary<string, Size> EmoticonSizes = new ConcurrentDictionary<string, Size>();
        /// <summary>
        /// The raw text property.
        /// </summary>
        public static readonly DependencyProperty RawTextProperty =
            DependencyProperty.RegisterAttached("RawText", typeof(string), typeof(RichTextBlockExtension), new PropertyMetadata("", OnRawTextChanged));

        /// <summary>
        /// Gets the raw text.
        /// </summary>
        public static string GetRawText(DependencyObject obj)
        {
            return (string)obj.GetValue(RawTextProperty);
        }

        /// <summary>
        /// Sets the raw text.
        /// </summary>
        public static void SetRawText(DependencyObject obj, string value)
        {
            obj.SetValue(RawTextProperty, value);
        }

        /// <summary>
        /// Sets the raw text.
        /// </summary>
        /// <remarks>Extension method.</remarks>
        public static void AssignRawText(this RichTextBlock rtb, string value)
        {
            rtb.SetValue(RawTextProperty, value);
        }

        /// <summary>
        /// Called when raw text changed.
        /// </summary>
        private static void OnRawTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            //if (DesignMode.DesignModeEnabled)
            //    return;
            if (e.NewValue == null)
                return;
            RichTextBlock rtb = d as RichTextBlock;
            if (rtb != null)
            {
                MessageModel msg = rtb.DataContext as MessageModel;
                if (msg == null)
                    return;
                rtb.Blocks.Clear();
                //string oldText = rtb.Tag != null ? rtb.Tag.ToString() : "";
                //string newText = e.NewValue.ToString().TrimStart(oldText);
                Stopwatch sw = Stopwatch.StartNew();
                List<MessageTextObjectMatch> objects = SplitUrlAndEmoticons(e.NewValue.ToString(), msg.SenderID);
                Debug.WriteLine("SplitUrlAndEmoticons: " + sw.ElapsedMilliseconds + "ms");
                sw.Restart();
                var paragraph = MakeParagraph(objects);
                Debug.WriteLine("MakeParagraph: " + sw.ElapsedMilliseconds + "ms");
                rtb.Blocks.Add(paragraph);
                // rtb.Tag = msg.MessageText;
            }
        }



        public static List<MessageTextObjectMatch> GetSplittedMessageItems(DependencyObject obj)
        {
            return (List<MessageTextObjectMatch>)obj.GetValue(SplittedMessageItemsProperty);
        }

        public static void SetSplittedMessageItems(DependencyObject obj, List<MessageTextObjectMatch> value)
        {
            obj.SetValue(SplittedMessageItemsProperty, value);
        }

        // Using a DependencyProperty as the backing store for SplittedMessageItems.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SplittedMessageItemsProperty =
            DependencyProperty.RegisterAttached("SplittedMessageItems", typeof(List<MessageTextObjectMatch>), typeof(RichTextBlockExtension), new PropertyMetadata(null, SplittedMessageItemsChanged));

        private static void SplittedMessageItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue == null)
                return;
            RichTextBlock rtb = d as RichTextBlock;
            if (rtb != null)
            {

                var objects = e.NewValue as List<MessageTextObjectMatch>;
                // Stopwatch sw = Stopwatch.StartNew();
                var paragraph = MakeParagraph(objects);
                // Debug.WriteLine("MakeParagraph: " + sw.ElapsedMilliseconds + "ms");
                rtb.Blocks.Clear();
                rtb.Blocks.Add(paragraph);
            }
            else
            {
                Debug.WriteLine("rtb is null");
            }
        }



        private static string GetImgurId(Uri link)
        {
            if (DesignMode.DesignModeEnabled)
                return null;
            string id = null;
            Regex imgurRegex = new Regex(@"http://(?:i\.imgur\.com/(?<id>.*?)\.(?:jpg|JPG|jpeg|JPEG|png|PNG|gif|GIF).*|imgur\.com/(?:gallery/)?(?<id>.*))$");
            Match imgurMatch = imgurRegex.Match(link.AbsoluteUri);
            if (imgurMatch.Success)
                id = imgurMatch.Groups["id"].Value;

            if (id != null && id.Contains("a/"))
                id = null;
            if (id != null && id.Contains("/"))
            {
                id = id.Substring(id.LastIndexOf("/", System.StringComparison.Ordinal));
                id = id.Replace("/", "");
            }
            if (link.AbsoluteUri.ToLower().Contains("/gallery"))
                id = null;
            return id;
        }

        /// <summary>
        /// Creates a paragraph with inlines built from the objects dictionary
        /// </summary>
        /// <param name="objects"></param>
        /// <returns></returns>
        private static Paragraph MakeParagraph(List<MessageTextObjectMatch> objects)
        {
            //if (DesignMode.DesignModeEnabled)
            //    return null;
            Paragraph p = new Paragraph();
            // int letterCount = 0;
            //bool tooLongText = false;
            foreach (MessageTextObjectMatch match in objects)
            {
                switch (match.MatchType)
                {
                    case MessageTextObjectMatchType.Text:
                        {
                            string txt = (string)match.Item;// kvp.Value.ToString();

                            RichTextBlock rtb = new RichTextBlock();
                            //Paragraph measureP = new Paragraph();
                            //measureP.Inlines.Add(new Run(){ Text=txt});
                            //rtb.Blocks.Add(measureP);


                            //if (txt.Length > StaticSettings.MaxMessageLength)
                            //{
                            //    tooLongText = true;
                            //    txt = txt.Substring(0, StaticSettings.MaxMessageLength);
                            //    txt += "...";
                            //}

                            if (!String.IsNullOrEmpty(txt))
                            {
                                Run r = new Run();
                                r.Text = txt;
                                p.Inlines.Add(r);
                                //  letterCount += r.Text.Length;
                            }
                            break;
                        }
                    case MessageTextObjectMatchType.Hyperlink:
                        {
                            //Hyperlink hl = kvp.Value as Hyperlink;
                            KeyValuePair<string, Uri> hlKvp = (KeyValuePair<string, Uri>)match.Item;
                            Hyperlink hl = new Hyperlink()
                            {
                                NavigateUri = hlKvp.Value,
                                //   TargetName = "_blank"
                            };
                            hl.Inlines.Add(new Run() { Text = hlKvp.Key });
                            hl.Foreground = new SolidColorBrush(Color.FromArgb(255, 0, 0, 238));

                            string imgurId = GetImgurId(hl.NavigateUri);
                            if (!String.IsNullOrEmpty(imgurId) && StaticSettings.IsWifiEnabled)
                            {

                                string thumbUrl = String.Format("Http://i.imgur.com/{0}b.png", imgurId);
                                //  WebClient client = new WebClient();


                                InlineUIContainer cont = new InlineUIContainer();
                                //  KeyValuePair<Hyperlink, InlineUIContainer> kv = new KeyValuePair<Hyperlink, InlineUIContainer>(hl, ui);

                                //  client.OpenReadCompleted += client_OpenReadCompleted;
                                Image dummy = new Image()
                                {
                                    Source = new BitmapImage(new Uri("ms-appx:///Assets/Images/imagedummy_160x160.png")),
                                    Stretch = Stretch.Uniform,
                                };

                                dummy.Loaded += dummy_Loaded;
                                dummy.HorizontalAlignment = HorizontalAlignment.Center;

                                cont.Child = dummy;

                                p.Inlines.Add(cont);
                                DownloadImage(cont, thumbUrl, hl);
                                //  client.OpenReadAsync(new Uri(thumbUrl), kv);
                            }
                            else
                            {
                                p.Inlines.Add(hl);
                            }
                            break;
                        }
                    case MessageTextObjectMatchType.CustomEmoticon:
                    case MessageTextObjectMatchType.DefaultEmoticon:
                        {
                            //  BitmapImage i = (BitmapImage)kvp.Value;
                            //using (TimeIt t = new TimeIt("adding emoticon"))
                            //{
                            Image image = new Image();// { Source = imageSource };
                            Uri emoticonUri = (Uri)match.Item;

                            BitmapImage imageSource = new BitmapImage();
                            imageSource.UriSource = emoticonUri;
                            image.Source = imageSource;
                            image.Visibility = Visibility.Collapsed;
                            image.Opacity = 0;
                            image.ImageOpened += image_ImageOpened;
                            // image.Tag = emoticonUri;
                            //  Size imgSize = new Size();
                            //if (EmoticonSizes.TryGetValue(emoticonUri.AbsoluteUri, out imgSize))
                            //{
                            //    image.Width = imgSize.Width;
                            //    image.Height = imgSize.Height;
                            //   // Debug.WriteLine(string.Format("set size of {0} to {1}", emoticonUri.AbsoluteUri, imgSize));
                            //}

                            //Image image = new Image() { Source = new BitmapImage(new Uri("ms-appx:///Assets/Images/imagedummy_160x160.png")) };
                            InlineUIContainer cont = new InlineUIContainer();
                            cont.Child = image;
                            p.Inlines.Add(cont);
                            //}
                            break;
                        }
                }

            }


            //if (tooLongText)
            //{
            //    InlineUIContainer cont = new InlineUIContainer();

            //    Button bn = new Button();

            //    bn.Content = "...";
            //    bn.Background = new SolidColorBrush(Colors.Yellow);
            //    bn.Height = 20;
            //    bn.Click += (s, args) =>
            //        {

            //        };
            //    var style = new Style(typeof(Button));
            //    style.BasedOn = bn.Style;
            //    style.Setters.Add(new Setter(Button.MarginProperty, new Thickness(0)));
            //    bn.Style = style;

            //    cont.Child = bn;
            //    p.Inlines.Add(new LineBreak());
            //    p.Inlines.Add(cont);
            //}
            return p;
        }

        static void dummy_Loaded(object sender, RoutedEventArgs e)
        {
            Image img = (Image)sender;
            img.Loaded -= dummy_Loaded;
        }

        private static void DownloadImage(InlineUIContainer cont, string thumbUrl, Hyperlink hl)
        {
            if (DesignMode.DesignModeEnabled)
                return;
            Task.Factory.StartNew(async () =>
                {
                    //  string imgUrl = String.Format("Http://i.imgur.com/{0}.png",e.UserState);
                    //    KeyValuePair<Hyperlink, InlineUIContainer> kvp = (KeyValuePair<Hyperlink, InlineUIContainer>)e.UserState;

                    // Hyperlink link = kvp.Key;
                    //  InlineUIContainer ui = kvp.Value;
                    HttpClient client = new HttpClient();
                    string fileName = Path.GetFileName(thumbUrl);

                    byte[] source = await StaticMethods.LoadImageData(fileName, ApplicationData.Current.TemporaryFolder).ConfigureAwait(false);
                    if (source == null)
                    {
                        HttpResponseMessage response = await client.GetAsync(new Uri(thumbUrl));
                        if (response.IsSuccessStatusCode)
                        {
                            source = await response.Content.ReadAsByteArrayAsync();
                            await StaticMethods.SaveImageToStorage(ApplicationData.Current.TemporaryFolder, source, fileName);
                        }

                    }
                    if (source == null)
                        return;
                    await DispatcherHelper.RunAsync(async () =>
                        {
                            RichTextBlock rtb = new RichTextBlock();

                            //   p.Inlines.Add(hl);
                            //    rtb.Blocks.Add(p);
                            // ui.Child = rtb;


                            Image img = new Image
                            {
                                Stretch = Stretch.Uniform,


                            };
                            try
                            {
                                var bi = await StaticMethods.ByteArrayToBitmapImage(source);
                                //var bi = new BitmapImage();
                                //bi.SetSource(stream);
                                img.Source = bi;
                                cont.Child = img;
                                // this.currentParagraph.Inlines.Add(new InlineUIContainer() { Child = img });
                                img.Tapped += async (s, args) =>
                                {
                                    //var task = new WebBrowserTask() { Uri = link.NavigateUri };
                                    //task.Show();
                                    await Windows.System.Launcher.LaunchUriAsync(hl.NavigateUri);

                                    args.Handled = true;
                                };
                                img.Holding += (s, args) =>
                                {

                                    Messenger.Default.Send(new NotificationMessage<string>(cont,"", "NYI","MessageDialog"));
                                    args.Handled = true;
                                };
                            }
                            catch (Exception err)
                            {
                                Debug.WriteLine("Error opening image:{0}", err);
                            }
                            finally
                            {
                                //stream.Close();
                                //client.OpenReadCompleted -= client_OpenReadCompleted;
                            }
                        });
                });

        }


        /// <summary>
        /// Parses the input text, and creates a dictionary containing the match start index as key, and a TextMatch object as value.
        /// </summary>
        /// <param name="text"></param>
        /// <param name="userID"></param>
        /// <returns></returns>
        public static List<MessageTextObjectMatch> SplitUrlAndEmoticons(string text, string userID)
        {
            //if (DesignMode.DesignModeEnabled)
            //    return null;
            // Dictionary<int, object> objects = new Dictionary<int, object>();
            List<MessageTextObjectMatch> matches = new List<MessageTextObjectMatch>();
            Regex UrlMatch = new Regex(@"(((file|gopher|news|nntp|telnet|http|ftp|https|ftps|sftp)://)|(www\.))+(([a-zA-ZÆæÅåØø0-9\._-]+\.[a-zA-Z]{2,6})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(/[\w!\&amp;%_\./\~\-\(\),\'\[\]+=#«»“”‘’?–]*)?");//new Regex(HyperLinkRegex);
            MatchCollection col = UrlMatch.Matches(text);
            Dictionary<int, TextMatch> foundTextMatches = new Dictionary<int, TextMatch>();
            foreach (Match m in col)
            {
                foundTextMatches.Add(m.Index, new TextMatch() { type = TextMatchType.URL, Text = m.Value });
            }
            Dictionary<string, IEmoticon> defaultEmoticons = null;
            if (DesignMode.DesignModeEnabled)
                defaultEmoticons = new Dictionary<string, IEmoticon>();
            else
                defaultEmoticons = VmHelper.Locator.CurrentClient.GetEmoticons(userID, true, false, true);
            string[] defaultKeys = defaultEmoticons.Keys.ToArray();
            foreach (string str in defaultKeys)
            {
                if (String.IsNullOrEmpty(defaultEmoticons[str].EmoticonPath))
                    continue;
                List<int> indexes = text.AllIndexesOf(str);
                foreach (int i in indexes)
                {
                    foundTextMatches.Add(i, new TextMatch() { type = TextMatchType.DefaultEmoticon, Text = str });
                }
            }

            Dictionary<string, IEmoticon> customEmoticons;
            if (DesignMode.DesignModeEnabled)
            {
                customEmoticons = new Dictionary<string, IEmoticon>();
            }
            else
            {
                customEmoticons = VmHelper.Locator.CurrentClient.GetEmoticons(userID, false, true, false);
                string[] customKeys = customEmoticons.Keys.ToArray();
                foreach (string str in customKeys)
                {
                    if (String.IsNullOrEmpty(customEmoticons[str].EmoticonPath))
                        continue;
                    List<int> indexes = text.AllIndexesOf(str);
                    foreach (int i in indexes)
                    {
                        if (!foundTextMatches.ContainsKey(i))
                            foundTextMatches.Add(i, new TextMatch() { type = TextMatchType.CustomEmoticon, Text = str });
                    }
                }
            }
            int startindex = 0;

            foreach (KeyValuePair<int, TextMatch> kvp in foundTextMatches.OrderBy(x => x.Key))
            {
                if (kvp.Key < startindex)
                    continue;

                string leading = text.Substring(startindex, kvp.Key - startindex);
                if (!String.IsNullOrEmpty(leading))
                    matches.Add(new MessageTextObjectMatch()
                    {
                        Position = startindex,
                        Item = leading,
                        MatchWord = kvp.Value.Text,
                        MatchType = MessageTextObjectMatchType.Text
                    });//objects.Add(startindex, leading);
                switch (kvp.Value.type)
                {
                    case TextMatchType.CustomEmoticon:
                        {
                            string keyword = kvp.Value.Text;

                            //Stopwatch watch = Stopwatch.StartNew();
                            //   Image image = new Image();//await StaticMethods.LoadImageFromIsolatedStorage(new Uri(savePath));
                            //  image.Source = new BitmapImage(new Uri(emoticon.EmoticonPath));//"ms-appdata:///Local/" + savePath));
                            //Debug.WriteLine(string.Format("Time to load custom emoticon {0}: {1}ms", emoticon, watch.ElapsedMilliseconds));
                            //  image.Stretch = Stretch.Uniform;
                            //image.Tag = emoticon;
                            //int width = 0;
                            //int height = 0;
                            //BitmapImage imgSource = image.Source as BitmapImage;

                            //if (imgSource != null)
                            //{
                            //    width = imgSource.PixelWidth;
                            //    height = imgSource.PixelHeight;
                            //}
                            //image.Width = width > 0 ? width * 1.5 : 22;
                            //image.Height = height > 0 ? height * 1.5 : 22;
                            Uri u = new Uri(customEmoticons[keyword].EmoticonPath);
                            //  objects.Add(kvp.Key, new BitmapImage(u));
                            matches.Add(new MessageTextObjectMatch()
                            {
                                Position = kvp.Key,
                                Item = u,
                                 MatchWord=kvp.Value.Text,
                                MatchType = MessageTextObjectMatchType.CustomEmoticon
                            });
                            break;
                        }
                    case TextMatchType.DefaultEmoticon:
                        {
                            string emoticon = kvp.Value.Text;
                            Stopwatch watch = Stopwatch.StartNew();
                            //    var savePath = "ms-appx:///Assets/Emoticons/";
                            Uri u = new Uri(defaultEmoticons[emoticon].EmoticonPath);
                            //Image image = new Image
                            //{
                            //    Source = new BitmapImage(u),
                            //    Stretch = Stretch.Uniform,
                            //};
                            //   Debug.WriteLine(string.Format("Time to load default emoticon {0}: {1}ms", emoticon, watch.ElapsedMilliseconds));
                            //int width = 0;
                            //int height = 0;
                            //BitmapImage imgSource = image.Source as BitmapImage;
                            //if (imgSource != null)
                            //{
                            //    width = imgSource.PixelWidth;
                            //    height = imgSource.PixelHeight;
                            //}
                            //image.Width = width > 0 ? width * 1.5 : 22;
                            //image.Height = height > 0 ? height * 1.5 : 22;

                            //image.Tag = emoticon;
                            matches.Add(new MessageTextObjectMatch()
                            {
                                Position = kvp.Key,
                                Item = u,
                                MatchWord = kvp.Value.Text,
                                MatchType = MessageTextObjectMatchType.DefaultEmoticon
                            });
                            //   objects.Add(kvp.Key, new BitmapImage(u));
                            break;
                        }
                    case TextMatchType.URL:
                        {
                            string url = kvp.Value.Text;
                            if (url.ToLower().StartsWith("www"))
                                url = "http://" + url;
                            //Hyperlink link = new Hyperlink()
                            //{
                            //    NavigateUri = new Uri(url, UriKind.Absolute),
                            //    //   TargetName = "_blank"
                            //};
                            //link.Inlines.Add(new Run() { Text = kvp.Value.Text });
                            //link.Foreground = new SolidColorBrush(Color.FromArgb(255, 0, 0, 238));
                            KeyValuePair<string, Uri> val = new KeyValuePair<string, Uri>(kvp.Value.Text, new Uri(url, UriKind.Absolute));

                            //objects.Add(kvp.Key, val);
                            matches.Add(new MessageTextObjectMatch()
                            {
                                Position = kvp.Key,
                                Item = val,
                                MatchWord = kvp.Value.Text,
                                MatchType = MessageTextObjectMatchType.Hyperlink
                            });
                            break;
                        }
                }
                startindex = kvp.Key + kvp.Value.Text.Length;
            }


            if (foundTextMatches.Count == 0)
                // objects.Add(0, text);
                matches.Add(new MessageTextObjectMatch()
                {
                    Position = 0,
                    Item = text,
                    MatchWord = text,
                    MatchType = MessageTextObjectMatchType.Text
                });
            else
            {
                string lastText = text.Substring(startindex, text.Length - startindex);
                if (!String.IsNullOrEmpty(lastText))
                    matches.Add(new MessageTextObjectMatch()
                    {
                        Position = startindex,
                        Item = lastText,
                        MatchWord = lastText,
                        MatchType = MessageTextObjectMatchType.Text
                    });
                //objects.Add(startindex, lastText);
            }
            return matches;
        }

        static void image_ImageOpened(object sender, RoutedEventArgs e)
        {
            Image image = sender as Image;
            // Uri imgUri = image.Tag as Uri;
            image.Stretch = Stretch.Uniform;
            image.ImageOpened -= image_ImageOpened;
            BitmapImage imgSource = image.Source as BitmapImage;
            Uri uriSource = ((BitmapImage)image.Source).UriSource;
            int maxWidth = 100;
            int maxHeight = 100;
            int width = 0;
            int height = 0;
            // Size storedSize = new Size();
            //if (EmoticonSizes.TryGetValue(imgSource.UriSource.AbsoluteUri, out storedSize))
            //{
            //    image.Width = storedSize.Width;
            //    image.Height = storedSize.Height;
            //}
            //else
            //{

            if (imgSource != null)
            {
                width = imgSource.PixelWidth;
                height = imgSource.PixelHeight;

                if (width > maxWidth)
                {
                    //  height =  height* (width / maxWidth);
                    width = maxWidth; //(int)Math.Round(width * 0.5);
                    image.Width = width > 0 ? width : 40;
                    ///(int)Math.Round(height * 0.5);
                }
                else if (height > maxHeight)
                {
                    // width = width* (height / maxHeight);
                    height = maxHeight; //(int)Math.Round(width * 0.5);
                    image.Height = height > 0 ? height : 40;
                    //height = (int)Math.Round(height * 0.5);
                    //width = (int)Math.Round(width * 0.5);
                }
                else
                {
                    image.Width = width;
                    image.Height = height;
                }
                //EmoticonSizes[imgSource.UriSource.AbsoluteUri] = new Size(width, height);
                //}
            }
            //  image.Width = width > 0 ? width : 40;
            // image.Height = height > 0 ? height : 40;

            //image.Width = width > 30 ? width * 0.5 : 22;
            //image.Height = height > 30 ? height * 0.5 : 22;
            image.Visibility = Visibility.Visible;
            //string ext = Path.GetExtension(uriSource.AbsolutePath);
            //if (ext == ".gif")
            //{
            //    XamlAnimatedGif.AnimationBehavior.SetAutoStart(image, true);
            //    XamlAnimatedGif.AnimationBehavior.SetSourceUri(image, new Uri(uriSource.AbsoluteUri.Replace(":/", ":///")));//"ms-appx://" + uriSource.AbsolutePath));//new Uri(src.UriSource.AbsoluteUri));
            //}
            image.Opacity = 1;
            // image.Animate(0, 1, "Opacity", 500, 0);
        }
        private enum TextMatchType
        {
            URL,
            CustomEmoticon,
            DefaultEmoticon
        }
        private struct TextMatch
        {
            public TextMatchType type { get; set; }
            public string Text { get; set; }
            public override string ToString()
            {
                return Text;
            }
        }
    }
}
